package com.jzo2o.orders.manager.service.impl;

import cn.hutool.db.sql.Order;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.util.BeanUtil;
import com.fasterxml.jackson.databind.util.ObjectBuffer;
import com.jzo2o.api.customer.dto.response.AddressBookResDTO;
import com.jzo2o.api.foundations.ServeApi;
import com.jzo2o.api.foundations.dto.response.ServeAggregationResDTO;
import com.jzo2o.api.market.dto.CouponApi;
import com.jzo2o.api.market.dto.request.CouponUseReqDTO;
import com.jzo2o.api.market.dto.response.AvailableCouponsResDTO;
import com.jzo2o.api.market.dto.response.CouponUseResDTO;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.model.msg.TradeStatusMsg;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.NumberUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.orders.base.config.OrderStateMachine;
import com.jzo2o.orders.base.constants.RedisConstants;
import com.jzo2o.orders.base.enums.OrderPayStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusChangeEventEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.base.model.dto.OrderSnapshotDTO;
import com.jzo2o.orders.manager.model.dto.request.OrdersPayReqDTO;
import com.jzo2o.orders.manager.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.orders.manager.model.dto.response.OrdersPayResDTO;
import com.jzo2o.orders.manager.model.dto.response.PlaceOrderResDTO;
import com.jzo2o.orders.manager.porperties.TradeProperties;
import com.jzo2o.orders.manager.service.IOrdersCreateService;
import com.jzo2o.orders.manager.service.client.CustomerClient;
import com.jzo2o.orders.manager.service.client.FoundationServeClient;
import com.jzo2o.orders.manager.service.client.MarketClient;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.weaver.ast.Or;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

/**
 * <p>
 * 下单服务类
 * </p>
 *
 * @author itcast
 * @since 2023-07-10
 */
@Slf4j
@Service
public class OrdersCreateServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersCreateService {
    @Resource
    private CustomerClient customerClient;

    @Resource
    private FoundationServeClient foundationServeClient;
    @Resource
    private ServeApi serveApi;


    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private OrdersCreateServiceImpl owner;

    @Resource
    private NativePayApi nativePayApi;

    @Resource
    private TradeProperties tradeProperties;

    @Resource
    private TradingApi tradingApi;

    @Resource
    private OrderStateMachine orderStateMachine;


    @Resource
    private MarketClient marketClient;

    @Resource
    private CouponApi couponApi;

    /**
     * 生成订单id 格式{yyMMdd}{13位id}
     * @return
     */
    private Long generateOrderId(){

        //调用redis自增一个序号
        Long increment = redisTemplate.opsForValue().increment(RedisConstants.Lock.ORDERS_SHARD_KEY_ID_GENERATOR);

        long orderId = DateUtils.getFormatDate(LocalDateTime.now(), "yyMMdd") * 10000000000000L + increment;

        return orderId;

    }


    @Override
    public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO placeOrderReqDTO){
        //服务地址信息需要远程调用jzo2o-cumstomer服务查询地址薄信息
        AddressBookResDTO addressBookResDTO = customerClient.getDetail(placeOrderReqDTO.getAddressBookId());

        //远程调用foundations服务查询服务相关信息
        //ServeAggregationResDTO serveAggregationResDTO = serveApi.findById(placeOrderReqDTO.getServeId());
        ServeAggregationResDTO serveAggregationResDTO = foundationServeClient.getDetail(placeOrderReqDTO.getServeId());
        if (serveAggregationResDTO == null || serveAggregationResDTO.getSaleStatus() != 2) {
            throw new BadRequestException("服务不可用");
        }
        // 准备组装数据
        Orders orders = new Orders();
        //订单基本信息，订单号，订单状态
        Long orderId = generateOrderId();
        orders.setId(orderId);
        //下单人id
        orders.setUserId(UserContext.currentUserId());
        //服务类型id
        orders.setServeTypeId(serveAggregationResDTO.getServeTypeId());
        //服务类型名称
        orders.setServeTypeName(serveAggregationResDTO.getServeTypeName());
        //服务项id
        orders.setServeItemId(serveAggregationResDTO.getServeItemId());
        //服务项名称
        orders.setServeItemName(serveAggregationResDTO.getServeItemName());
        //服务项图片
        orders.setServeItemImg(serveAggregationResDTO.getServeItemImg());
        //服务单位
        orders.setUnit(serveAggregationResDTO.getUnit());
        //服务id
        orders.setServeId(placeOrderReqDTO.getServeId());
        //订单状态
        orders.setOrdersStatus(OrderStatusEnum.NO_PAY.getStatus());
        //支付状态
        orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());
        //单价
        orders.setPrice(serveAggregationResDTO.getPrice());
        //购买数量
        orders.setPurNum(placeOrderReqDTO.getPurNum());
        //总价
        BigDecimal totalAmount = serveAggregationResDTO.getPrice().multiply(new BigDecimal(placeOrderReqDTO.getPurNum()));
        orders.setTotalAmount(totalAmount);
        //优惠价格
        orders.setDiscountAmount(BigDecimal.ZERO);
        //实际价格
        orders.setRealPayAmount(NumberUtils.sub(orders.getTotalAmount(), orders.getDiscountAmount()));

        //city_code
        orders.setCityCode(serveAggregationResDTO.getCityCode());
        //服务地址
        String serveAddress = new StringBuffer().append(addressBookResDTO.getProvince())
                .append(addressBookResDTO.getCity())
                .append(addressBookResDTO.getCounty())
                .append(addressBookResDTO.getAddress())
                .toString();
        orders.setServeAddress(serveAddress);
        //联系电话
        orders.setContactsPhone(addressBookResDTO.getPhone());
        //联系人姓名
        orders.setContactsName(addressBookResDTO.getName());
        //服务开始时间
        orders.setServeStartTime(placeOrderReqDTO.getServeStartTime());
        //经纬度
        orders.setLon(addressBookResDTO.getLon());
        orders.setLat(addressBookResDTO.getLat());
        //排序字段
        //排序字段，根据服务开始时间转为毫秒时间戳+订单后5位
        long sortBy = DateUtils.toEpochMilli(orders.getServeStartTime()) + orders.getId() % 100000;
        orders.setSortBy(sortBy);

        //判断如果使用了优惠券进行核销
        if(ObjectUtils.isNotNull(placeOrderReqDTO.getCouponId())){
            owner.addWithCoupon(orders,placeOrderReqDTO.getCouponId());
        }else{
            //保存订单
            owner.add(orders);

        }





        //返回数据
        PlaceOrderResDTO placeOrderResDTO = new PlaceOrderResDTO();
        placeOrderResDTO.setId(orders.getId());

        return placeOrderResDTO;
    }

//    @Transactional(rollbackFor = Exception.class)
    @GlobalTransactional//开启全局事务
    public void addWithCoupon(Orders orders, Long couponId) {
        CouponUseReqDTO couponUseReqDTO = new CouponUseReqDTO();
        couponUseReqDTO.setOrdersId(orders.getId());
        couponUseReqDTO.setId(couponId);
        couponUseReqDTO.setTotalAmount(orders.getTotalAmount());
        //优惠券核销
        CouponUseResDTO couponUseResDTO = couponApi.use(couponUseReqDTO);
        // 设置优惠金额
        orders.setDiscountAmount(couponUseResDTO.getDiscountAmount());
        // 计算实付金额
        BigDecimal realPayAmount = orders.getTotalAmount().subtract(orders.getDiscountAmount());
        orders.setRealPayAmount(realPayAmount);
        //保存订单
        add(orders);
    }

    @Override
    public OrdersPayResDTO pay(Long id, OrdersPayReqDTO ordersPayReqDTO) {
        //查一下订单
        Orders orders = getById(id);

        //如果订单不存在抛出异常

        if(ObjectUtils.isNull(orders)){
            throw new CommonException("订单不存在");
        }

        //如果订单已经支付成功直接返回
        Integer payStatus = orders.getPayStatus();//支付状态
        if(payStatus == OrderPayStatusEnum.PAY_SUCCESS.getStatus() && ObjectUtils.isNotEmpty(orders.getTradingOrderNo())){
            OrdersPayResDTO ordersPayResDTO = BeanUtils.toBean(orders, OrdersPayResDTO.class);
            ordersPayResDTO.setProductOrderNo(orders.getId());
            return ordersPayResDTO;
        }else{
            //请求支付服务生成二维码
            NativePayResDTO nativePayResDTO = generateOrderQrCode(orders, ordersPayReqDTO.getTradingChannel());
            OrdersPayResDTO ordersPayResDTO = BeanUtils.toBean(nativePayResDTO, OrdersPayResDTO.class);

            //更新订单表中的交易单号和支付渠道
            boolean update = lambdaUpdate()
                    .eq(Orders::getId, id)
                    .set(Orders::getTradingOrderNo, nativePayResDTO.getTradingOrderNo())
                    .set(Orders::getTradingChannel, nativePayResDTO.getTradingChannel())
                    .update();
            if(!update){
                log.info("请求支付服务生成认为吗，更新订单：{}的交易 单号和支付渠道失败", id);
                throw new CommonException("请求支付服务生成二维码，更新订单：" + id + "的交易单号和支付渠道失败");
            }

            return ordersPayResDTO;
        }

    }

    @Override
    public OrdersPayResDTO getPayResultFromTradServer(Long id) {
        //查一下订单
        Orders orders = getById(id);

        //如果订单不存在抛出异常
        if(ObjectUtils.isNull(orders)){
            throw new CommonException("订单不存在");
        }
        //支付结果
        Integer payStatus = orders.getPayStatus();
        //如果未支付才去请求支付服务拿支付结果
        if(payStatus == OrderPayStatusEnum.NO_PAY.getStatus() && ObjectUtils.isNotEmpty(orders.getTradingOrderNo())){
            //拿到交易单号
            Long tradingOrderNo = orders.getTradingOrderNo();
            //根据交易单号请求支付服务的查询结果接口
            TradingResDTO tradResultByTradingOrderNo = tradingApi.findTradResultByTradingOrderNo(tradingOrderNo);
            //如果支付成功，更新数据库
            if(ObjectUtils.equal(tradResultByTradingOrderNo.getTradingState(), TradingStateEnum.YJS)){
                TradeStatusMsg tradeStatusMsg = new TradeStatusMsg();
                //订单id
                tradeStatusMsg.setProductOrderNo(id);
                //支付渠道
                tradeStatusMsg.setTradingChannel(tradResultByTradingOrderNo.getTradingChannel());
                //交易单号
                tradeStatusMsg.setTradingOrderNo(tradResultByTradingOrderNo.getTradingOrderNo());
                //第三方（微信）的交易单号
                tradeStatusMsg.setTransactionId(tradResultByTradingOrderNo.getTransactionId());
                //支付成功code
                tradeStatusMsg.setStatusCode(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
                //支付成功名称
                tradeStatusMsg.setStatusName(OrderPayStatusEnum.PAY_SUCCESS.name());
                //更新数据库
                owner.paySuccess(tradeStatusMsg);
                //构造返回数据
                OrdersPayResDTO ordersPayResDTO = BeanUtils.toBean(tradeStatusMsg, OrdersPayResDTO.class);

                return ordersPayResDTO;
            }
        }

        OrdersPayResDTO ordersPayResDTO = BeanUtils.toBean(orders, OrdersPayResDTO.class);


        return ordersPayResDTO;
    }

    @Override
    @Transactional
    public void paySuccess(TradeStatusMsg tradeStatusMsg) {
        //支付成功更新订单表
//        boolean update = lambdaUpdate()
//                .eq(Orders::getId, tradeStatusMsg.getProductOrderNo()) //订单id
//                .eq(Orders::getOrdersStatus, 0) //订单状态只能由待支付变为派单中
//                .set(Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus()) //更新为支付成功
//                .set(Orders::getTransactionId, tradeStatusMsg.getTransactionId())
//                .set(Orders::getOrdersStatus, OrderStatusEnum.DISPATCHING.getStatus()) //订单状态为派单中
//                .set(Orders::getPayTime, LocalDateTime.now())
//                .update();
//        if(!update){
//            throw new CommonException("支付成功更新订单状态:"+ tradeStatusMsg.getProductOrderNo() +"为派单中失败");
//        }

        //订单
        Orders orders = getById(tradeStatusMsg.getProductOrderNo());

        //使用状态机将待支付状态改为派单中
        //Long dbShardId, String bizId, StatusChangeEvent statusChangeEventEnum, T bizSnapshot
        OrderSnapshotDTO orderSnapshotDTO = new OrderSnapshotDTO();
        orderSnapshotDTO.setTradingOrderNo(tradeStatusMsg.getTradingOrderNo());
        orderSnapshotDTO.setTradingChannel(tradeStatusMsg.getTradingChannel());
        orderSnapshotDTO.setPayTime(LocalDateTime.now());
        orderSnapshotDTO.setThirdOrderId(tradeStatusMsg.getTransactionId());

        orderStateMachine.changeStatus(orders.getUserId(), tradeStatusMsg.getProductOrderNo().toString(), OrderStatusChangeEventEnum.PAYED,orderSnapshotDTO);
    }

    @Override
    public List<Orders> queryOverTimePayOrdersListByCount(Integer count) {
        //sql select * from orders where orders_status = 0 and create_time < 当前时间 - 15分钟
        List<Orders> list = lambdaQuery()
                .eq(Orders::getOrdersStatus, OrderStatusEnum.NO_PAY.getStatus())
                .lt(Orders::getCreateTime, LocalDateTime.now().minusMinutes(15))
                .last("limit" + count)
                .list();

        return list;
    }

    @Override
    public List<AvailableCouponsResDTO> getAvailableCoupons(Long serveId, Integer purNum) {
        //远程调用foundations
        ServeAggregationResDTO serveAggregationResDTO = serveApi.findById(serveId);
        if(ObjectUtils.isNull(serveAggregationResDTO)){
            throw new CommonException("服务信息不存在");
        }

        //计算订单总价
        BigDecimal price = serveAggregationResDTO.getPrice();
        //计算订单总价
        BigDecimal totalAmount = price.multiply(new BigDecimal(purNum));

        //远程调用优惠券服务查询可用优惠券
        List<AvailableCouponsResDTO> available = marketClient.getAvailable(totalAmount);

        //返回可用优惠券列表
        return available;
    }

    /**
     *
     * @param orders
     * @param tradingChannel
     * @return
     */

    private NativePayResDTO generateOrderQrCode(Orders orders, PayChannelEnum tradingChannel){
        //封装请求支付服务的参数
        NativePayReqDTO nativePay = new NativePayReqDTO();

        //根据请求的支付渠道需要使用哪个商户号
        Long enterprisedId = ObjectUtils.equal(tradingChannel, PayChannelEnum.WECHAT_PAY) ? tradeProperties.getWechatEnterpriseId() : tradeProperties.getAliEnterpriseId();
        nativePay.setEnterpriseId(enterprisedId);
        //家政的订单号
        nativePay.setProductOrderNo(orders.getId());

        //金额
        nativePay.setTradingAmount(orders.getRealPayAmount());

        //业务系统标识，家政订单服务请求支付服务统一指定jzo2o.orders
        nativePay.setProductAppId("jzo2o.orders");
        //请求的支付渠道
        nativePay.setTradingChannel(tradingChannel);
        //是否切换支付渠道
        //首先拿到当前orders中的支付渠道
        //判断如果在订单中有支付渠道并且和新传入支付渠道不一致，说明用户切换支付渠道
        String tradingChannel1 = orders.getTradingChannel();
        if(ObjectUtils.isNotEmpty(orders.getTradingChannel()) && ObjectUtils.notEqual(orders.getTradingChannel(), tradingChannel.getValue())){
            nativePay.setChangeChannel(true);
        }
        //备注，服务，项名称
        nativePay.setMemo(orders.getServeItemName());


        //请求支付服务生成二维码
        NativePayResDTO downLineTrading = nativePayApi.createDownLineTrading(nativePay);
        //如果生成二维码成功，需要拿到支付服务返回的交易单号，更新到
        if(ObjectUtils.isNotEmpty(downLineTrading)){
            return downLineTrading;
        }
        throw new CommonException("请求支付服务生成二维码失败");
    }





    @Transactional(rollbackFor = Exception.class)
    public void add(Orders orders){
        //插入数据库
        boolean save = this.save(orders);

        if(!save){
            throw new CommonException("下单失败");
        }
        //调用状态机启动方法
        //参数里欸包 Long dbShardId 分库分表的时候要用，String bizId 订单id， T bizSnapshot 订单快照
        OrderSnapshotDTO orderSnapshotDTO = BeanUtils.toBean(getById(orders.getId()), OrderSnapshotDTO.class);
        orderStateMachine.start(orders.getUserId(), orders.getId().toString(), orderSnapshotDTO);
    }
}
